﻿namespace com.savealot
{
    using System;
    using System.Threading;

    [Serializable]
    public class AsyncEventHandler
    {
        #region Methods

        /// <summary>
        /// Attempt to raise the specified event asynchronously in a recognized safe manner.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="eventHandler">The method that will handle an event.</param>
        public static void TryRaise( object sender, EventHandler eventHandler )
        {
            TryRaise( sender, eventHandler, EventArgs.Empty );
        }

        /// <summary>
        /// Attempt to raise the specified event asynchronously in a recognized safe manner.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="eventHandler">The method that will handle an event.</param>
        /// <param name="eventArgs">An instance of type EventArgs.</param>
        public static void TryRaise( object sender, EventHandler eventHandler, EventArgs eventArgs )
        {
            EventHandler t = eventHandler;
            if ( t != null )
            {
                InternalEventAction internalEventAction = new InternalEventAction
                {
                    eventArgs = eventArgs,
                    eventHandler = t,
                    sender = sender
                };
                AsyncCallback callback = new AsyncCallback( internalCallback );
                AsyncResult asyncResult = new AsyncResult( internalEventAction );
                callback.BeginInvoke( asyncResult, null, null );
            }
        }

        static void internalCallback( object o )
        {
            InternalEventAction iea = ( o as IAsyncResult ).AsyncState as InternalEventAction;
            EventHandler eventHandler = iea.eventHandler;
            var sender = iea.sender;
            EventArgs eventArgs = iea.eventArgs;

            if ( eventHandler != null )
                eventHandler( sender, eventArgs );
        }

        #endregion Methods

        #region Nested Types

        internal class AsyncResult : IAsyncResult
        {
            #region Constructors

            public AsyncResult( object asyncState )
            {
                AsyncState = asyncState;
            }

            #endregion Constructors

            #region Properties

            public object AsyncState
            {
                get;
                private set;
            }

            public WaitHandle AsyncWaitHandle
            {
                get
                {
                    throw new NotImplementedException( );
                }
            }

            public bool CompletedSynchronously
            {
                get
                {
                    throw new NotImplementedException( );
                }
            }

            public bool IsCompleted
            {
                get
                {
                    throw new NotImplementedException( );
                }
            }

            #endregion Properties
        }

        internal class InternalEventAction<E> : InternalEventAction
            where E : EventArgs
        {
            #region Properties

            internal new E eventArgs
            {
                get;
                set;
            }

            internal new EventHandler<E> eventHandler
            {
                get;
                set;
            }

            #endregion Properties
        }

        internal class InternalEventAction
        {
            #region Properties

            internal EventArgs eventArgs
            {
                get;
                set;
            }

            internal EventHandler eventHandler
            {
                get;
                set;
            }

            internal object sender
            {
                get;
                set;
            }

            #endregion Properties
        }

        #endregion Nested Types
    }

    [Serializable]
    public class AsyncEventHandler<E> : AsyncEventHandler
        where E : EventArgs
    {
        #region Methods

        /// <summary>
        /// Attempt to raise the specified event asynchronously in a recognized safe manner.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="eventHandler">The method that will handle an event.</param>
        /// <param name="eventArgs">An instance of type &lt;E&gt; where E is a subclass of EventArgs.</param>
        public static void TryRaise( object sender, EventHandler<E> eventHandler, E eventArgs )
        {
            EventHandler<E> t = eventHandler;
            if ( t != null )
            {
                InternalEventAction<E> internalEventAction = new InternalEventAction<E>
                {
                    eventArgs = eventArgs,
                    eventHandler = t,
                    sender = sender
                };
                AsyncCallback callback = new AsyncCallback( internalCallback );
                AsyncResult asyncResult = new AsyncResult( internalEventAction );
                callback.BeginInvoke( asyncResult, null, null );
            }
        }

        static void internalCallback( object o )
        {
            InternalEventAction<E> iea = ( o as IAsyncResult ).AsyncState as InternalEventAction<E>;
            EventHandler<E> eventHandler = iea.eventHandler;
            var sender = iea.sender;
            E eventArgs = iea.eventArgs;

            if ( eventHandler != null )
                eventHandler( sender, eventArgs );
        }

        #endregion Methods
    }
}